package com.ae.putingotdown.client;

import com.ae.putingotdown.Config;
import com.ae.putingotdown.block.PlacedIngot;
import com.ae.putingotdown.block.Register;
import com.ae.putingotdown.block.TilePlacedItem;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.BlockRendererDispatcher;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.RenderTypeLookup;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Quaternion;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraft.util.math.vector.Vector3i;
import net.minecraftforge.client.model.data.EmptyModelData;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Random;

public class TERPlacedIngot extends TileEntityRenderer<TilePlacedItem> {
    private final Random random = new Random();
    private static final BlockState STATE = Register.INGOT.get().defaultBlockState();
    private final Minecraft mc = Minecraft.getInstance();
    private final ItemRenderer itemRenderer = mc.getItemRenderer();
    private final BlockRendererDispatcher blockRenderer = mc.getBlockRenderer();

    public TERPlacedIngot(TileEntityRendererDispatcher dispatcher) {
        super(dispatcher);
    }

    @Override
    public void render(@Nonnull TilePlacedItem tile, float partialTicks, @Nonnull MatrixStack matrixStack, @Nonnull IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn) {
        ClientPlayerEntity player = mc.player;

        if (player == null) {
            return;
        }

        Vector3d position = player.position();
        double distance = Math.sqrt(tile.getBlockPos().distSqr(new Vector3i(position.x, position.y, position.z)));

        //超过设置距离就不进行渲染
        if (distance > Config.DISTANCE.get()) {
            return;
        }

        int displayCount = PlacedIngot.getDisplayCount(tile);

        if (displayCount <= 0) {
            return;
        }

        int color = tile.getColor();

        if (color != -1) {
            renderIngots(matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, blockRenderer, displayCount, color);
        } else {
            BlockState state = tile.getBlockState();
            ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
            ItemStack stack = tile.handler.getStackInSlot();
            IBakedModel model = itemRenderer.getModel(stack, tile.getLevel(), null);

            if (model.isGui3d()) {
                render3DItems(stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, displayCount, state);
            } else {
                renderItems(stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, displayCount, state);
            }
        }
    }

    private void render3DItems(ItemStack stack, MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn, int displayCount, BlockState stateIn) {
        int i = (int) Math.ceil(displayCount / 8d);
        double high = displayCount > 8 ? 0.03125 : 0;
        high = i > 4 ? 0.5 : high;

        if (i < 8) {
            int x;

            for (int j = 1; j <= (i > 4 ? i - 4 : i); j++) {
                x = j % 4 == 0 ? 4 : j % 4;
                renderItem(0.25 * (x % 2 == 0 ? 1 : 3), high + 0.2, 0.25 * (x - 2 > 0 ? 3 : 1), new Quaternion(0, 0, 0, 0), 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
            }
        } else {
            Direction face = stateIn.getValue(PlacedIngot.DIRECTION);
            switch (face.getOpposite()) {
                case DOWN:
                case UP:
                case NORTH:
                    renderItem3DTO2D(0.2, 0.18, -0.001, new Quaternion(Vector3f.YN, 180, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case SOUTH:
                    renderItem3DTO2D(0.8, 0.18, 1.001, new Quaternion(Vector3f.YN, 0, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case WEST:
                    renderItem3DTO2D(-0.001, 0.18, 0.8, new Quaternion(Vector3f.YN, 90, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case EAST:
                    renderItem3DTO2D(1.001, 0.18, 0.2, new Quaternion(Vector3f.YN, -90, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
            }
        }
/*      后面这两步可能是调整渲染时的光源朝向，用以解决模型过暗的问题，抄自抽屉。
        RenderHelper.setupFor3DItems();
        matrixStack.last().normal().load(Matrix3f.createScaleMatrix(1, -1, 1));
        当与其他ter同时在屏幕中出现时会出现阴影异常问题。*/
    }

    private void renderItems(ItemStack stack, MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn, int displayCount, BlockState stateIn) {
        Quaternion quaternion = new Quaternion(90, 0, -30, true);
        if (displayCount <= 8) {
            switch (displayCount) {
                case 8:
                    setQuaternion(110, 5f, -90, quaternion);
                    renderItem(0.4, 0.07, 0.8, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 7:
                    setQuaternion(85, 3, 95, quaternion);
                    renderItem(0.52600625, 0.05, 0.2, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 6:
                    setQuaternion(100, 170, 240, quaternion);
                    renderItem(0.8, 0.03, 0.8, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 5:
                    setQuaternion(90, 5, 110, quaternion);
                    renderItem(0.2, 0.033, 0.6, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 4:
                    setQuaternion(70, 0, 0, quaternion);
                    renderItem(0.7, 0.05, 0.3, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 3:
                    setQuaternion(90.5397f, -5.3154f, -6.9477f, quaternion);
                    renderItem(0.6, 0.044, 0.6, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 2:
                    setQuaternion(90, 0, 0, quaternion);
                    renderItem(0.2, 0.022, 0.2, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 1:
                    setQuaternion(90, 0, -30, quaternion);
                    renderItem(0.5, 0.022, 0.522875, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
            }
        } else if (displayCount <= 32) {
            int i = displayCount / 8;
            setQuaternion(90, 0, 0, quaternion);
            renderItem(0.5, 0.0625 + 0.03125, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
            switch (i) {
                case 4:
                    setQuaternion(90, 0, 0, quaternion);
                    renderItem(0.5, 0.3125 + 0.03125, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 3:
                    setQuaternion(90, 0, 90, quaternion);
                    renderItem(0.5, 0.25 + 0.03125, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 2:
                    setQuaternion(90, 0, 0, quaternion);
                    renderItem(0.5, 0.1875 + 0.03125, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 1:
                    setQuaternion(90, 0, 90, quaternion);
                    renderItem(0.5, 0.125 + 0.03125, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
            }
        } else if (displayCount <= 56) {
            int i = (displayCount - 32) / 8 + 1;
            switch (i) {
                default:
                case 3:
                    setQuaternion(90, 0, 90, quaternion);
                    renderItem(0.5, 0.1875 + 0.03125 + 0.5, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 2:
                    setQuaternion(90, 0, 0, quaternion);
                    renderItem(0.5, 0.125 + 0.03125 + 0.5, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 1:
                    setQuaternion(90, 0, 90, quaternion);
                    renderItem(0.5, 0.0625 + 0.03125 + 0.5, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
            }
        } else {
            matrixStack.pushPose();
            Direction face = stateIn.getValue(PlacedIngot.DIRECTION);
            switch (face.getOpposite()) {
                case DOWN:
                case UP:
                case NORTH:
                    renderItem(0.2, 0.18, 0.005, new Quaternion(Vector3f.XN, 0, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case SOUTH:
                    renderItem(0.8, 0.18, 0.995, new Quaternion(Vector3f.YN, 180, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case WEST:
                    renderItem(0.005, 0.18, 0.8, new Quaternion(Vector3f.YN, -90, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case EAST:
                    renderItem(0.995, 0.18, 0.2, new Quaternion(Vector3f.YN, 90, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
            }
            matrixStack.popPose();
        }
    }

    public void renderItem(double x, double y, double z, Quaternion rotate, float size, ItemStack stack, MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn) {
        matrixStack.pushPose();
        matrixStack.translate(x, y, z);
        matrixStack.mulPose(rotate);
        matrixStack.scale(size, size, size);
        IBakedModel model = itemRenderer.getModel(stack, null, null);
        itemRenderer.render(stack, ItemCameraTransforms.TransformType.FIXED, false, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, model);
        matrixStack.popPose();
    }

    public void renderItem3DTO2D(double x, double y, double z, Quaternion rotate, float size, ItemStack stack, MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn) {
        matrixStack.pushPose();
        matrixStack.translate(x, y, z);
        matrixStack.mulPose(rotate);
        matrixStack.scale(size, size, 0.001f);
        IBakedModel model = itemRenderer.getModel(stack, null, null);
        itemRenderer.render(stack, ItemCameraTransforms.TransformType.GUI, false, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, model);
        matrixStack.popPose();
    }

    private void renderIngots(MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn, BlockRendererDispatcher renderer, int displayCount, int color) {
        for (int i = 1; i <= displayCount; i++) {
            int y = i / 8;
            int x = i % 8;

            if (y > 0 && x == 0) {
                --y;
                x = 8;
            }

            switch (x) {
                case 1:
                case 5:
                    renderIngot(0d, y * 0.125d, x > 4 ? 0.5109375d : 0, color, y % 2 > 0, STATE, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, renderer);
                    break;
                case 2:
                case 6:
                    renderIngot(0.2546875d, y * 0.125d, x > 4 ? 0.5109375d : 0, color, y % 2 > 0, STATE, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, renderer);
                    break;
                case 3:
                case 7:
                    renderIngot(0.5109375d, y * 0.125d, x > 4 ? 0.5109375d : 0, color, y % 2 > 0, STATE, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, renderer);
                    break;
                case 4:
                case 8:
                    renderIngot(0.765625d, y * 0.125d, x > 4 ? 0.5109375d : 0, color, y % 2 > 0, STATE, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, renderer);
                    break;
            }
        }
    }

    public void renderIngot(double x, double y, double z, int color, boolean rotate, BlockState state, MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn, BlockRendererDispatcher render) {
        matrixStack.pushPose();
        if (rotate) {
            matrixStack.mulPose(new Quaternion(Vector3f.YN, 90, true));
        }
        matrixStack.translate(x, y, rotate ? z - 1 : z);
        renderBlock(render, color, state, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
        matrixStack.popPose();
    }

    private void renderBlock(BlockRendererDispatcher renderer, int color, BlockState state, MatrixStack matrixStack, IRenderTypeBuffer renderTypeBuffer, int combinedLightIn, int combinedOverlayIn) {
        BlockRenderType blockrendertype = state.getRenderShape();
        if (blockrendertype != BlockRenderType.INVISIBLE) {
            switch (blockrendertype) {
                case MODEL:
                    IBakedModel ibakedmodel = renderer.getBlockModel(state);
                    float r = (float) (color >> 16 & 255) / 255.0F;
                    float g = (float) (color >> 8 & 255) / 255.0F;
                    float b = (float) (color & 255) / 255.0F;
                    renderModel(matrixStack.last(), renderTypeBuffer.getBuffer(RenderTypeLookup.getRenderType(state, false)), state, ibakedmodel, r, g, b, combinedLightIn, combinedOverlayIn);
                    break;
                case ENTITYBLOCK_ANIMATED:
                    ItemStack stack = new ItemStack(state.getBlock());
                    stack.getItem().getItemStackTileEntityRenderer().renderByItem(stack, ItemCameraTransforms.TransformType.NONE, matrixStack, renderTypeBuffer, combinedLightIn, combinedOverlayIn);
            }
        }
    }

    private void renderModel(MatrixStack.Entry matrixEntry, IVertexBuilder vertexBuilder, @Nullable BlockState state, IBakedModel model, float r, float g, float b, int combinedLightIn, int combinedOverlayIn) {
        random.setSeed(42L);
        for (BakedQuad bakedquad : model.getQuads(state, null, random, EmptyModelData.INSTANCE)) {
            float f = MathHelper.clamp(r, 0.0F, 1.0F);
            float f1 = MathHelper.clamp(g, 0.0F, 1.0F);
            float f2 = MathHelper.clamp(b, 0.0F, 1.0F);
            vertexBuilder.putBulkData(matrixEntry, bakedquad, f, f1, f2, combinedLightIn, combinedOverlayIn);
        }
    }

    public void setQuaternion(float x, float y, float z, Quaternion quaternion) {
        x *= ((float) Math.PI / 180F);
        y *= ((float) Math.PI / 180F);
        z *= ((float) Math.PI / 180F);

        float f = (float) Math.sin(0.5F * x);
        float f1 = (float) Math.cos(0.5F * x);
        float f2 = (float) Math.sin(0.5F * y);
        float f3 = (float) Math.cos(0.5F * y);
        float f4 = (float) Math.sin(0.5F * z);
        float f5 = (float) Math.cos(0.5F * z);
        float i = f * f3 * f5 + f1 * f2 * f4;
        float j = f1 * f2 * f5 - f * f3 * f4;
        float k = f * f2 * f5 + f1 * f3 * f4;
        float r = f1 * f3 * f5 - f * f2 * f4;
        quaternion.set(i, j, k, r);
    }
}
